/*
 *   Copyright (c) 2013-2014 Gabriele Mariotti.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package it.gmariotti.cardslib.library.view;

import it.gmariotti.cardslib.library.AnimatorListenerAdapter;
import it.gmariotti.cardslib.library.Constants;
import it.gmariotti.cardslib.library.ResourceTable;
import it.gmariotti.cardslib.library.internal.Card;
import it.gmariotti.cardslib.library.internal.CardExpand;
import it.gmariotti.cardslib.library.internal.CardHeader;
import it.gmariotti.cardslib.library.internal.CardThumbnail;
import it.gmariotti.cardslib.library.internal.ViewToClickToExpand;
import it.gmariotti.cardslib.library.utils.PixelMapUtils;
import it.gmariotti.cardslib.library.view.base.CardViewWrapper;
import it.gmariotti.cardslib.library.view.component.CardHeaderView;
import it.gmariotti.cardslib.library.view.component.CardThumbnailView;
import it.gmariotti.cardslib.library.view.listener.SwipeDismissViewTouchListener;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ComponentTreeObserver;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;

import java.util.HashMap;


/**
 * Card view
 * </p>
 * Use an XML layout file to display it.
 * </p>
 * First, you need an XML layout that will display the Card.
 * Then create a model:
 * <pre><code>
 *
 *     //Create a Card
 *     Card card = new Card(getContext());
 *
 *     //Create a CardHeader
 *     CardHeader header = new CardHeader(getContext());
 *
 *     //Add Header to card
 *     card.addCardHeader(header);
 *
 * </code></pre>
 * Last get a reference to the `CardView` from your code, and set your `Card.
 * <pre><code>
 *     //Set card in the cardView
 *     CardView cardView = (CardView) getAbility().findViewById(ResourceTable.Id_carddemo);
 *
 *     cardView.setCard(card);
 * </code></pre>
 * You can easily build your layout.
 * </p>
 * The quickest way to start with this would be to copy one of this files and create your layout.
 * Then you can inflate your layout in the `CardView` using the attr: `ohos:card_layout_resourceID="@layout/my_layout`
 * Example:
 * </p>
 *
 * @author Gabriele Mariotti (gabri.mariotti@gmail.com)
 */
public class CardView extends BaseCardView implements CardViewWrapper {
    /**
     * {@link CardHeader} model
     */
    protected CardHeader mCardHeader;

    /**
     * {@link CardThumbnail} model
     */
    protected CardThumbnail mCardThumbnail;

    /**
     * {@link CardExpand} model
     */
    protected CardExpand mCardExpand;


    /**
     * Main Layout
     */
    protected Component mInternalMainCardLayout;

    /**
     * Content Layout
     */
    protected Component mInternalContentLayout;

    /**
     * Inner View.
     */
    protected Component mInternalInnerView;

    /**
     * Hidden layout used by expand/collapse action
     */
    protected Component mInternalExpandLayout;

    /**
     * Expand Inner view
     */
    protected Component mInternalExpandInnerView;


    /**
     * Animator to expand/collapse
     */
    protected AnimatorValue mExpandAnimator;

    /**
     * Listener invoked when Expand Animator starts
     * It is used internally
     */
    protected CardViewWrapper.OnExpandListAnimatorListener mOnExpandListAnimatorListener;

    private int cardBackgroundColor = 0;

    // Constructor
    public CardView(Context context) {
        super(context);
    }

    public CardView(Context context, AttrSet attrs) {
        super(context, attrs);
    }

    public CardView(Context context, AttrSet attrs, String defStyle) {
        super(context, attrs, defStyle);
    }


    /**
     * Init custom attrs.
     *
     * @param attrs    attrs
     * @param defStyle defStyle
     */
    protected void initAttrs(AttrSet attrs, String defStyle) {
        card_layout_resourceID = ResourceTable.Layout_card_layout;
        if (attrs.getAttr("card_layout_resourceID").isPresent()) {
            String s = attrs.getAttr("card_layout_resourceID").get().getStringValue();
            String id = s.split(":")[1];
            card_layout_resourceID = Integer.parseInt(id);
        }
        if (attrs.getAttr("cardBackgroundColor").isPresent()) {
            String color = attrs.getAttr("cardBackgroundColor").get().getStringValue();
            cardBackgroundColor = Color.getIntColor(color);
        }
    }


    /**
     * Add a {@link Card}.
     * It is very important to set all values and all components before launch this method.
     *
     * @param card {@link Card} model
     */
    @Override
    public void setCard(Card card) {
        super.setCard(card);
        if (card != null) {
            mCardHeader = card.getCardHeader();
            mCardThumbnail = card.getCardThumbnail();
            mCardExpand = card.getCardExpand();
        }

        //Retrieve all IDs
        if (!isRecycle()) {
            retrieveLayoutIDs();
        }

        //Build UI
        buildUI();
    }

    /**
     * Refreshes the card content (it doesn't inflate layouts again)
     *
     * @param card card
     */
    public void refreshCard(Card card) {
        mIsRecycle = true;
        setCard(card);
        mIsRecycle = false;
    }

    @Override
    public void setOnTouchListener(TouchEventListener onTouchListener) {
        setTouchEventListener(onTouchListener);
    }

    /**
     * Refreshes the card content and replaces the inner layout elements (it inflates layouts again!)
     *
     * @param card card
     */
    public void replaceCard(Card card) {
        mForceReplaceInnerLayout = true;
        refreshCard(card);
        mForceReplaceInnerLayout = false;
    }


    @Override
    protected void buildUI() {
        super.buildUI();

        mCard.setCardView(this);

        //Setup Header view
        setupHeaderView();

        //Setup Main View
        setupMainView();

//        //setup Thumbnail
        setupThumbnailView();

        //Setup Expand View
        setupExpandView();

        //Setup Supplemental Actions
        setupSupplementalActions();

        //Setup Listeners
        setupListeners();

        //Setup Expand Action
        setupExpandAction();

        //Setup Drawable Resources
        setupDrawableResources();
    }


    /**
     * Retrieve all Layouts IDs
     */
    @Override
    protected void retrieveLayoutIDs() {
        super.retrieveLayoutIDs();

        //Main Layout
        mInternalMainCardLayout = findComponentById(ResourceTable.Id_card_main_layout);

        //Get HeaderLayout
        mInternalHeaderLayout = (CardHeaderView) findComponentById(ResourceTable.Id_card_header_layout);

        //Get ExpandHiddenView
        mInternalExpandLayout = findComponentById(ResourceTable.Id_card_content_expand_layout);

        //Get ContentLayout
        mInternalContentLayout = findComponentById(ResourceTable.Id_card_main_content_layout);

        //Get ThumbnailLayout
        mInternalThumbnailLayout = (CardThumbnailView) findComponentById(ResourceTable.Id_card_thumbnail_layout);
    }

    /**
     * Setup Header View
     */
    protected void setupHeaderView() {
        if (mCardHeader != null) {

            if (mInternalHeaderLayout != null) {
                mInternalHeaderLayout.setVisibility(VISIBLE);

                //Set recycle value (very important in a ListView)
                mInternalHeaderLayout.setRecycle(isRecycle());
                mInternalHeaderLayout.setForceReplaceInnerLayout(isForceReplaceInnerLayout());
                //Add Header View
                mInternalHeaderLayout.addCardHeader(mCardHeader);

            }
        } else {
            //No header. Hide layouts
            if (mInternalHeaderLayout != null) {
                mInternalHeaderLayout.setVisibility(HIDE);

                if (isForceReplaceInnerLayout()) {
                    mInternalHeaderLayout.addCardHeader(null);
                }
            }
        }
    }

    /**
     * Setup the Main View
     */
    protected void setupMainView() {
        if (mInternalContentLayout != null) {

            ComponentContainer mParentGroup = null;
            try {
                mParentGroup = (ComponentContainer) mInternalContentLayout;
            } catch (Exception e) {
                setRecycle(false);
            }

            //Check if view can be recycled
            //It can happen in a listView, and improves performances
            if (!isRecycle() || isForceReplaceInnerLayout()) {

                if (isForceReplaceInnerLayout() && mInternalContentLayout != null && mInternalInnerView != null){
                    ((ComponentContainer) mInternalContentLayout).removeComponent(mInternalInnerView);
                }


                mInternalInnerView = mCard.getInnerView(getContext(), (ComponentContainer) mInternalContentLayout);
            } else {
                //View can be recycled.
                //Only setup Inner Elements
                if (mCard.getInnerLayout() > -1){
                    mCard.setupInnerViewElements(mParentGroup, mInternalInnerView);
                }
            }
        }
    }


    /**
     * Setup the Thumbnail View
     */
    protected void setupThumbnailView() {
        if (mInternalThumbnailLayout != null) {
            if (mCardThumbnail != null) {
                mInternalThumbnailLayout.setVisibility(VISIBLE);
                mInternalThumbnailLayout.setRecycle(isRecycle());
                mInternalThumbnailLayout.setForceReplaceInnerLayout(isForceReplaceInnerLayout());
                mInternalThumbnailLayout.addCardThumbnail(mCardThumbnail);
            } else {
                mInternalThumbnailLayout.setVisibility(HIDE);
            }
        }
    }

    /**
     * Setup Drawable Resources
     */
    protected void setupDrawableResources() {

        //Card
        if (mCard != null) {
            if (mCard.getBackgroundResourceId() != 0) {
                changeBackgroundResourceId(mCard.getBackgroundResourceId());
            } else if (mCard.getBackgroundResource() != null) {
                changeBackgroundResource(mCard.getBackgroundResource());
            }


            if (cardBackgroundColor != Card.DEFAULT_COLOR) {
                changeBackgroundColorResourceId(cardBackgroundColor);
            }

            if (mCard.getBackgroundColorResourceId() != Card.DEFAULT_COLOR) {
                changeBackgroundColorResourceId(mCard.getBackgroundColorResourceId());
            }
        }
    }

    /**
     * setupSupplementalActions
     */
    protected void setupSupplementalActions() {
        if (mCard != null){
            mCard.setupSupplementalActions();
        }

    }

    /**
     * setupExpandAction
     */
    protected void setupExpandAction() {

        //Config ExpandLayout and its animation
        if (mInternalExpandLayout != null && ((mCardHeader != null && mCardHeader.isButtonExpandVisible()) ||
                mCard.getViewToClickToExpand() != null)) {

            //todo 先暂时用addwindowBoundListener替换addOnPreDrawListener
            //Create the expand/collapse animator
            mInternalExpandLayout.getComponentTreeObserver().addWindowBoundListener(new ComponentTreeObserver.WindowBoundListener() {
                @Override
                public void onWindowBound() {
                    mInternalExpandLayout.getComponentTreeObserver().removeWindowBoundListener(this);
                    mExpandAnimator = ExpandCollapseHelper.createSlideAnimator((CardView) mCard.getCardView(), 0, mInternalExpandLayout.getHeight());
                }

                @Override
                public void onWindowUnbound() {

                }
            });
        }

        //Setup action and callback
        setupExpandCollapseActionListener();
    }

    /**
     * Setup All listeners
     */
    @SuppressWarnings("deprecation")
//    @SuppressLint("NewApi")
    protected void setupListeners() {

        //Swipe listener
        if (mCard.isSwipeable()) {
            this.setOnTouchListener(new SwipeDismissViewTouchListener(this, mCard, new SwipeDismissViewTouchListener.DismissCallbacks() {
                @Override
                public boolean canDismiss(Card card) {
                    return card.isSwipeable();
                }

                @Override
                public void onDismiss(CardViewWrapper cardView, Card card) {
                    final ComponentContainer vg = (ComponentContainer) (((Component) cardView).getComponentParent());
                    if (vg != null) {
                        vg.removeComponent((Component) cardView);
                        card.onSwipeCard();
                    }
                }
            }));
        } else {
            this.setOnTouchListener(null);
        }

        //OnClick listeners and partial listener

        //Reset Partial Listeners
        resetPartialListeners();

        if (mCard.isClickable()) {
            //Set the onClickListener
            if (!mCard.isMultiChoiceEnabled()) {
                if (mCard.getOnClickListener() != null) {
                    this.setClickedListener(v -> {
                        if (mCard.getOnClickListener() != null)
                            mCard.getOnClickListener().onClick(mCard, v);
                    });

                    //Prevent multiple events
                    //if (!mCard.isSwipeable() && mCard.getOnSwipeListener() == null) {
                    //    this.setClickable(true);
                    //}

                } else {
                    HashMap<Integer, Card.OnCardClickListener> mMultipleOnClickListner = mCard.getMultipleOnClickListener();
                    if (mMultipleOnClickListner != null && !mMultipleOnClickListner.isEmpty()) {

                        for (int key : mMultipleOnClickListner.keySet()) {
                            Component viewClickable = decodeAreaOnClickListener(key);
                            final Card.OnCardClickListener mListener = mMultipleOnClickListner.get(key);
                            if (viewClickable != null) {
                                //Add listener to this view
                                viewClickable.setClickedListener(v -> {
                                    //Callback to card listener
                                    if (mListener != null)
                                        mListener.onClick(mCard, v);
                                });

                                //Add Selector to this view
                                if (key > Card.CLICK_LISTENER_ALL_VIEW) {
                                    mHelperImpl.setCardSelector(viewClickable, new ShapeElement(getContext(), ResourceTable.Graphic_card_selector));
                                }
                            }
                        }
                    } else {
                        //There aren't listners
                        this.setClickable(false);
                    }
                }
            }
        } else {
            this.setClickable(false);
        }

        //LongClick listener
        if (mCard.isLongClickable()) {
            this.setLongClickedListener(component -> {
                if (mCard.getOnLongClickListener() != null)
                    mCard.getOnLongClickListener().onLongClick(mCard, component);
            });
        } else {
            this.setLongClickable(false);
        }
    }

    /**
     * Reset all partial listeners
     */
    protected void resetPartialListeners() {
        Component viewClickable = decodeAreaOnClickListener(Card.CLICK_LISTENER_HEADER_VIEW);
        if (viewClickable != null)
            viewClickable.setClickable(false);

        viewClickable = decodeAreaOnClickListener(Card.CLICK_LISTENER_THUMBNAIL_VIEW);
        if (viewClickable != null)
            viewClickable.setClickable(false);

        viewClickable = decodeAreaOnClickListener(Card.CLICK_LISTENER_CONTENT_VIEW);
        if (viewClickable != null)
            viewClickable.setClickable(false);
    }

    /**
     * decodeAreaOnClickListener
     *
     * @param area
     * @return Component
     */
    protected Component decodeAreaOnClickListener(int area) {

        Component view = null;

        switch (area) {
            case Card.CLICK_LISTENER_ALL_VIEW:
                view = this;
                break;
            case Card.CLICK_LISTENER_HEADER_VIEW:
                view = mInternalHeaderLayout;
                break;
            case Card.CLICK_LISTENER_THUMBNAIL_VIEW:
                view = mInternalThumbnailLayout;
                break;
            case Card.CLICK_LISTENER_CONTENT_VIEW:
                view = mInternalContentLayout;
                break;
            default:
                break;
        }
        return view;
    }


    /**
     * Add ClickListener to expand and collapse hidden view
     */
    protected void setupExpandCollapseActionListener() {
        if (mInternalExpandLayout != null) {
            mInternalExpandLayout.setVisibility(HIDE);

            boolean internal_blockForLongClickOnImageButtonExpand = false;
            ViewToClickToExpand viewToClickToExpand = null;

            //ButtonExpandVisible has a priority to viewClickToExpand
            if (mCardHeader != null && mCardHeader.isButtonExpandVisible()) {

                viewToClickToExpand = ViewToClickToExpand.builder()
                        .setupView(mInternalHeaderLayout.getImageButtonExpand())
                        .highlightView(true);
                internal_blockForLongClickOnImageButtonExpand = true;

            } else if (mCard.getViewToClickToExpand() != null) {

                viewToClickToExpand = mCard.getViewToClickToExpand();
            }

            if (viewToClickToExpand != null) {

                TitleViewOnClickListener titleViewOnClickListener = new TitleViewOnClickListener(mInternalExpandLayout, mCard, viewToClickToExpand.isViewToSelect());

                /*if (mCardHeader!=null && mCardHeader.isButtonExpandVisible() && mInternalHeaderLayout != null) {
                    mInternalHeaderLayout.setOnClickExpandCollapseActionListener(titleViewOnClickListener);
                }*/

                Component viewToClick = viewToClickToExpand.getViewToClick();
                if (viewToClick != null) {

                    if (internal_blockForLongClickOnImageButtonExpand) {
                        //The long click on Header button is now allowed
                        viewToClick.setClickedListener(titleViewOnClickListener);
                    } else {
                        if (viewToClickToExpand.isUseLongClick()) {
                            viewToClick.setLongClickedListener(new TitleViewOnLongClickListener(titleViewOnClickListener));
                        } else {
                            viewToClick.setClickedListener(titleViewOnClickListener);
                        }
                    }
                } else {
                    ViewToClickToExpand.CardElementUI cardElementUI = viewToClickToExpand.getCardElementUIToClick();
                    if (cardElementUI != null) {
                        switch (cardElementUI) {
                            case CARD:
                                viewToClick = this;
                                break;
                            case HEADER:
                                viewToClick = getInternalHeaderLayout();
                                break;
                            case THUMBNAIL:
                                viewToClick = getInternalThumbnailLayout();
                                break;
                            case MAIN_CONTENT:
                                viewToClick = getInternalContentLayout();
                                break;
                        }
                        if (viewToClick != null) {
                            if (viewToClickToExpand.isUseLongClick()) {
                                viewToClick.setLongClickedListener(new TitleViewOnLongClickListener(titleViewOnClickListener));
                            } else {
                                viewToClick.setClickedListener(titleViewOnClickListener);
                            }
                        }
                    }
                }

                if (isExpanded()) {
                    //Make layout visible and button selected
                    mInternalExpandLayout.setVisibility(VISIBLE);
                    if (viewToClick != null) {
                        if (viewToClickToExpand.isViewToSelect())
                            viewToClick.setSelected(true);
                    }
                } else {
                    //Make layout hidden and button not selected
                    mInternalExpandLayout.setVisibility(HIDE);
                    if (viewToClick != null) {
                        if (viewToClickToExpand.isViewToSelect())
                            viewToClick.setSelected(false);
                    }
                }
            }

        }
    }

    /**
     * Setup Expand View
     */
    protected void setupExpandView() {
        if (mInternalExpandLayout != null && mCardExpand != null) {

            //Check if view can be recycled
            //It can happen in a listView, and improves performances
            if (!isRecycle() || isForceReplaceInnerLayout()) {

                if (isForceReplaceInnerLayout() && mInternalExpandLayout != null && mInternalExpandInnerView != null)
                    ((ComponentContainer) mInternalExpandLayout).removeComponent(mInternalExpandInnerView);

                mInternalExpandInnerView = mCardExpand.getInnerView(getContext(), (ComponentContainer) mInternalExpandLayout);
            } else {
                //View can be recycled.
                //Only setup Inner Elements
                if (mCardExpand.getInnerLayout() > -1)
                    mCardExpand.setupInnerViewElements((ComponentContainer) mInternalExpandLayout, mInternalExpandInnerView);
            }

            ComponentContainer.LayoutConfig layoutParams = mInternalExpandLayout.getLayoutConfig();
            layoutParams.height = ComponentContainer.LayoutConfig.MATCH_CONTENT;
            mInternalExpandLayout.setLayoutConfig(layoutParams);
        }
    }

    /**
     * doToggleExpand
     */
    public void doToggleExpand() {

        if (mInternalExpandLayout != null) {
            ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard, false);

            boolean isVisible = mInternalExpandLayout.getVisibility() == VISIBLE;
            if (isVisible) {
                ExpandCollapseHelper.animateCollapsing(helper);
            } else {
                ExpandCollapseHelper.animateExpanding(helper);
            }
        }
    }

    /**
     * doExpand
     */
    public void doExpand() {

        if (mInternalExpandLayout != null) {
            ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard, false);

            boolean isVisible = mInternalExpandLayout.getVisibility() == VISIBLE;
            if (!isVisible) {
                ExpandCollapseHelper.animateExpanding(helper);
            }
        }
    }

    /**
     * doCollapse
     */
    public void doCollapse() {

        if (mInternalExpandLayout != null) {
            ExpandContainerHelper helper = new ExpandContainerHelper(mInternalExpandLayout, mCard, false);

            boolean isVisible = mInternalExpandLayout.getVisibility() == VISIBLE;
            if (isVisible) {
                ExpandCollapseHelper.animateCollapsing(helper);
            }
        }
    }

    /**
     * Listener to expand/collapse hidden Expand Layout
     * It starts animation
     */
    protected class TitleViewOnClickListener implements ClickedListener {

        ExpandContainerHelper mExpandContainerHelper;

        private TitleViewOnClickListener(Component contentParent, Card card) {
            this(contentParent, card, true);
        }

        private TitleViewOnClickListener(Component contentParent, Card card, boolean viewToSelect) {
            mExpandContainerHelper = new ExpandContainerHelper(contentParent, card, viewToSelect);
        }

        @Override
        public void onClick(Component view) {
            boolean isVisible = mExpandContainerHelper.contentParent.getVisibility() == VISIBLE;
            if (isVisible) {
                ExpandCollapseHelper.animateCollapsing(mExpandContainerHelper);
                if (mExpandContainerHelper.viewToSelect)
                    view.setSelected(false);
            } else {
                ExpandCollapseHelper.animateExpanding(mExpandContainerHelper);
                if (mExpandContainerHelper.viewToSelect)
                    view.setSelected(true);
            }
        }
    }

    /**
     * Listener to expand/collapse hidden Expand Layout
     * It starts animation
     */
    protected class TitleViewOnLongClickListener implements LongClickedListener {

        TitleViewOnClickListener mOnClickListener;

        private TitleViewOnLongClickListener(TitleViewOnClickListener onClickListener) {
            mOnClickListener = onClickListener;
        }

        @Override
        public void onLongClicked(Component component) {
            if (mOnClickListener != null) {
                mOnClickListener.onClick(component);
            }
        }
    }

    private class ExpandContainerHelper {

        private Component contentParent;
        private Card card;
        private boolean viewToSelect = true;

        private ExpandContainerHelper(Component contentParent, Card card, boolean viewToSelect) {
            this.contentParent = contentParent;
            this.card = card;
            this.viewToSelect = viewToSelect;
        }

        /**
         * getCardView
         *
         * @return CardView
         */
        public CardView getCardView() {
            return (CardView) card.getCardView();
        }
    }


    private static class ExpandCollapseHelper {

        /**
         * Expanding animator.
         *
         * @param helper
         */
        private static void animateExpanding(final ExpandContainerHelper helper) {

            //Callback
            if (helper.card.getOnExpandAnimatorStartListener() != null)
                helper.card.getOnExpandAnimatorStartListener().onExpandStart(helper.card);

            if (helper.getCardView().getOnExpandListAnimatorListener() != null) {
                //List Animator
                helper.getCardView().getOnExpandListAnimatorListener().onExpandStart(helper.getCardView(), helper.contentParent);
            } else {
                //Std animator
                helper.contentParent.setVisibility(VISIBLE);
                if (helper.getCardView().mExpandAnimator != null) {
                    helper.getCardView().mExpandAnimator.setStateChangedListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onEnd(Animator animator) {
                            super.onEnd(animator);
                            helper.card.setExpanded(true);
                            //Callback
                            if (helper.card.getOnExpandAnimatorEndListener() != null)
                                helper.card.getOnExpandAnimatorEndListener().onExpandEnd(helper.card);
                        }
                    });
                    helper.getCardView().mExpandAnimator.start();
                } else {
                    if (helper.card.getOnExpandAnimatorEndListener() != null)
                        helper.card.getOnExpandAnimatorEndListener().onExpandEnd(helper.card);
                    HiLog.warn(Constants.LABEL_LOG, "Does the card have the ViewToClickToExpand?");
                }
            }
        }

        /**
         * Collapse animator
         *
         * @param helper
         */
        private static void animateCollapsing(final ExpandContainerHelper helper) {

            //Callback
            if (helper.card.getOnCollapseAnimatorStartListener() != null)
                helper.card.getOnCollapseAnimatorStartListener().onCollapseStart(helper.card);

            if (helper.getCardView().getOnExpandListAnimatorListener() != null) {
                //There is a List Animator.
                helper.getCardView().getOnExpandListAnimatorListener().onCollapseStart(helper.getCardView(), helper.contentParent);
            } else {
                //Std animator
                int origHeight = helper.contentParent.getHeight();

                AnimatorValue animator = createSlideAnimator(helper.getCardView(), origHeight, 0);
                animator.setStateChangedListener(new Animator.StateChangedListener() {
                    @Override
                    public void onStart(Animator animator) {
                        //nothing to do
                    }

                    @Override
                    public void onStop(Animator animator) {
                        //nothing to do
                    }

                    @Override
                    public void onCancel(Animator animator) {
                        //nothing to do
                    }

                    @Override
                    public void onEnd(Animator animator) {
                        helper.contentParent.setVisibility(HIDE);
                        helper.card.setExpanded(false);
                        //Callback
                        if (helper.card.getOnCollapseAnimatorEndListener() != null)
                            helper.card.getOnCollapseAnimatorEndListener().onCollapseEnd(helper.card);
                    }

                    @Override
                    public void onPause(Animator animator) {
                        //nothing to do
                    }

                    @Override
                    public void onResume(Animator animator) {
                        //nothing to do
                    }
                });
                animator.start();
            }
        }


        /**
         * Create the Slide Animator invoked when the expand/collapse button is clicked
         *
         * @param cardView cardView
         * @param start    start value
         * @param end      end value
         * @return AnimatorValue
         */
        protected static AnimatorValue createSlideAnimator(final CardView cardView, int start, int end) {
            AnimatorValue animator = new AnimatorValue();
            animator.setValueUpdateListener((animatorValue, v) -> {
                int value;
                if (end > start) {
                    value = (int) (start + (end - start) * v);
                } else {
                    value = (int) (end + (start - end) * (1 - v));
                }
                ComponentContainer.LayoutConfig layoutParams = cardView.mInternalExpandLayout.getLayoutConfig();
                layoutParams.height = value;
                cardView.mInternalExpandLayout.setLayoutConfig(layoutParams);
            });
            return animator;
        }

    }

    // -------------------------------------------------------------
    //  OnExpandListAnimator Interface and Listener
    // -------------------------------------------------------------

    /**
     * Returns the listener invoked when expand/collpase animation starts
     * It is used internally
     *
     * @return listener
     */
    public OnExpandListAnimatorListener getOnExpandListAnimatorListener() {
        return mOnExpandListAnimatorListener;
    }

    /**
     * Sets the listener invoked when expand/collapse animation starts
     * It is used internally. Don't override it.
     *
     * @param onExpandListAnimatorListener listener
     */
    public void setOnExpandListAnimatorListener(OnExpandListAnimatorListener onExpandListAnimatorListener) {
        this.mOnExpandListAnimatorListener = onExpandListAnimatorListener;
    }


    /**
     * FIXME
     *
     * @return Component
     */
    public Component getInternalContentLayout() {
        return mInternalContentLayout;
    }


    /**
     * Indicates if the card is expanded or collapsed
     *
     * @return <code>true</code> if the card is expanded
     */
    public boolean isExpanded() {
        if (mCard != null) {
            return mCard.isExpanded();
        } else
            return false;
    }

    /**
     * Sets the card as expanded or collapsed
     *
     * @param expanded <code>true</code> if the card is expanded
     */
    public void setExpanded(boolean expanded) {
        if (mCard != null) {
            mCard.setExpanded(expanded);
        }
    }

    @Override
    public boolean isNative() {
        return false;
    }

    /**
     * Retrieves the InternalMainCardGlobalLayout.
     * Background style is applied here.
     *
     * @return Component
     */
    public Component getInternalMainCardLayout() {
        return mInternalMainCardLayout;
    }

    /**
     * Changes dynamically the drawable resource to override the style of MainLayout.
     *
     * @param drawableResourceId drawable resource Id
     */
    @Override
    public void changeBackgroundResourceId(int drawableResourceId) {
        try {
            if (drawableResourceId != 0) {
                if (mInternalMainCardLayout != null) {
                    mInternalMainCardLayout.setBackground(PixelMapUtils.prepareElementById(mContext, drawableResourceId));
                }
            }
        } catch (Exception e) {
            HiLog.error(Constants.LABEL_LOG, e.toString());
        }
    }

    /**
     * Changes dynamically the drawable resource to override the style of MainLayout.
     *
     * @param drawableResource drawable resource
     */
    @Override
    public void changeBackgroundResource(Element drawableResource) {
        if (drawableResource != null) {
            if (mInternalMainCardLayout != null) {
                mHelperImpl.setBackground(mInternalMainCardLayout, drawableResource);
            }
        }
    }

    @Override
    public void changeBackgroundColorResourceId(int colorResourceId) {
        try {
            ShapeElement shapeElement = new ShapeElement();
            RgbColor rgbColor = new RgbColor(getResourceManager().getElement(colorResourceId).getColor());
            shapeElement.setRgbColor(rgbColor);
            mInternalMainCardLayout.setBackground(shapeElement);
        } catch (Exception e) {
            HiLog.error(Constants.LABEL_LOG, e.toString());
        }
    }
}
